package org.htmlparser.filters;

import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.htmlparser.Node;
import org.htmlparser.NodeFilter;
import org.htmlparser.Tag;
import org.htmlparser.util.NodeList;

public class CssSelectorNodeFilter
  implements NodeFilter
{
  private static final int COMBINATOR = 5;
  private static final int COMMA = 7;
  private static final int COMMENT = 1;
  private static final int DELIM = 6;
  private static final int NAME = 4;
  private static final int QUOTEDSTRING = 2;
  private static final int RELATION = 3;
  private static Pattern tokens = Pattern.compile("(/\\*.*?\\*/) | (   \".*?[^\"]\" | '.*?[^']' | \"\" | '' ) | ( [\\~\\*\\$\\^]? = ) | ( [a-zA-Z_\\*](?:[a-zA-Z0-9_-]|\\\\.)* ) | \\s*( [+>~\\s] )\\s* | ( [\\.\\[\\]\\#\\:)(] ) | ( [\\,] ) | ( . )", 38);
  private Matcher m = null;
  private NodeFilter therule;
  private String token;
  private int tokentype = 0;

  public CssSelectorNodeFilter(String paramString)
  {
    this.token = null;
    this.m = tokens.matcher(paramString);
    if (nextToken())
      this.therule = parse();
  }

  private boolean nextToken()
  {
    int i;
    int j;
    if ((this.m != null) && (this.m.find()))
    {
      j = 1;
      if (j < this.m.groupCount())
        if (this.m.group(j) != null)
        {
          this.tokentype = j;
          this.token = this.m.group(j);
          i = 1;
        }
    }
    while (true)
    {
      while (true)
      {
        return i;
        ++j;
      }
      this.tokentype = 0;
      this.token = null;
      i = 0;
    }
  }

  private NodeFilter parse()
  {
    Object localObject = null;
    switch (this.tokentype)
    {
    case 2:
    case 3:
    default:
    case 1:
    case 4:
    case 6:
    case 5:
    case 7:
    }
    while (true)
    {
      while (true)
      {
        while (true)
        {
          if (this.token != null);
          return localObject;
          if (localObject != null)
            break;
          localObject = parseSimple();
        }
        localObject = new AndFilter((NodeFilter)localObject, parseSimple());
      }
      switch (this.token.charAt(0))
      {
      default:
        localObject = new HasAncestorFilter((NodeFilter)localObject);
      case '+':
      case '>':
      }
      while (true)
      {
        while (true)
        {
          while (true)
            nextToken();
          localObject = new AdjacentFilter((NodeFilter)localObject);
        }
        localObject = new HasParentFilter((NodeFilter)localObject);
      }
      OrFilter localOrFilter = new OrFilter((NodeFilter)localObject, parse());
      nextToken();
      localObject = localOrFilter;
    }
  }

  private NodeFilter parseAttributeExp()
  {
    String str1;
    Object localObject = null;
    if (this.tokentype == 4)
    {
      str1 = this.token;
      nextToken();
      if (!("]".equals(this.token)))
        break label85;
      localObject = new HasAttributeFilter(unescape(str1));
    }
    while (true)
    {
      label85: String str2;
      String str3;
      do
      {
        do
        {
          if (localObject != null)
            break label248;
          throw new IllegalArgumentException("Syntax error at " + this.token + this.tokentype);
        }
        while (this.tokentype != 3);
        str2 = null;
        str3 = this.token;
        nextToken();
        if (this.tokentype == 2)
          str2 = unescape(this.token.substring(1, this.token.length() - 1));
        while (true)
        {
          do
            while (true)
            {
              if ((!("~=".equals(str3))) || (str2 == null))
                break label219;
              localObject = new AttribMatchFilter(unescape(str1), "\\b" + str2.replaceAll("([^a-zA-Z0-9])", "\\\\$1") + "\\b");
            }
          while (this.tokentype != 4);
          label219: str2 = unescape(this.token);
        }
      }
      while ((!("=".equals(str3))) || (str2 == null));
      localObject = new HasAttributeFilter(str1, str2);
    }
    label248: nextToken();
    return ((NodeFilter)localObject);
  }

  private NodeFilter parsePseudoClass()
  {
    throw new IllegalArgumentException("pseudoclasses not implemented yet");
  }

  private NodeFilter parseSimple()
  {
    int i = 0;
    Object localObject = null;
    if (this.token != null);
    switch (this.tokentype)
    {
    case 2:
    case 3:
    case 5:
    default:
      i = 1;
    case 1:
      while (true)
      {
        if ((i == 0) && (this.token != null));
        return localObject;
        nextToken();
      }
    case 4:
      if ("*".equals(this.token))
        localObject = new YesFilter(null);
      while (true)
      {
        while (true)
        {
          while (true)
            nextToken();
          if (localObject != null)
            break;
          localObject = new TagNameFilter(unescape(this.token));
        }
        localObject = new AndFilter((NodeFilter)localObject, new TagNameFilter(unescape(this.token)));
      }
    case 6:
    }
    switch (this.token.charAt(0))
    {
    default:
    case '.':
    case '#':
    case ':':
    case '[':
    }
    while (true)
    {
      while (true)
      {
        while (true)
        {
          while (true)
          {
            while (true)
            {
              while (true)
              {
                while (true)
                {
                  while (true)
                  {
                    while (true)
                      nextToken();
                    nextToken();
                    if (this.tokentype != 4)
                      throw new IllegalArgumentException("Syntax error at " + this.token);
                    if (localObject != null)
                      break;
                    localObject = new HasAttributeFilter("class", unescape(this.token));
                  }
                  localObject = new AndFilter((NodeFilter)localObject, new HasAttributeFilter("class", unescape(this.token)));
                }
                nextToken();
                if (this.tokentype != 4)
                  throw new IllegalArgumentException("Syntax error at " + this.token);
                if (localObject != null)
                  break;
                localObject = new HasAttributeFilter("id", unescape(this.token));
              }
              localObject = new AndFilter((NodeFilter)localObject, new HasAttributeFilter("id", unescape(this.token)));
            }
            nextToken();
            if (localObject != null)
              break;
            localObject = parsePseudoClass();
          }
          localObject = new AndFilter((NodeFilter)localObject, parsePseudoClass());
        }
        nextToken();
        if (localObject != null)
          break;
        localObject = parseAttributeExp();
      }
      localObject = new AndFilter((NodeFilter)localObject, parseAttributeExp());
    }
  }

  public static String unescape(String paramString)
  {
    StringBuffer localStringBuffer = new StringBuffer(paramString.length());
    Matcher localMatcher = Pattern.compile("\\\\(?:([a-fA-F0-9]{2,6})|(.))").matcher(paramString);
    while (true)
    {
      do
        while (true)
        {
          if (!(localMatcher.find()))
            break label82;
          if (localMatcher.group(1) == null)
            break;
          localMatcher.appendReplacement(localStringBuffer, String.valueOf((char)Integer.parseInt(localMatcher.group(1), 16)));
        }
      while (localMatcher.group(2) == null);
      localMatcher.appendReplacement(localStringBuffer, localMatcher.group(2));
    }
    label82: localMatcher.appendTail(localStringBuffer);
    return localStringBuffer.toString();
  }

  public boolean accept(Node paramNode)
  {
    return this.therule.accept(paramNode);
  }

  private static class AttribMatchFilter
  implements NodeFilter
  {
    private String attrib;
    private Pattern rel;

    public AttribMatchFilter(String paramString1, String paramString2)
    {
      this.rel = Pattern.compile(paramString2);
      this.attrib = paramString1;
    }

    public boolean accept(Node paramNode)
    {
      int i;
      if ((paramNode instanceof Tag) && (((Tag)paramNode).getAttribute(this.attrib) != null))
        if ((this.rel != null) && (!(this.rel.matcher(((Tag)paramNode).getAttribute(this.attrib)).find())))
          i = 0;
      while (true)
      {
        while (true)
        {
          return i;
          i = 1;
        }
        i = 0;
      }
    }
  }

  private static class YesFilter
  implements NodeFilter
  {
    public boolean accept(Node paramNode)
    {
      return true;
    }
  }

  private static class AdjacentFilter
  implements NodeFilter
  {
    private NodeFilter sibtest;

    public AdjacentFilter(NodeFilter paramNodeFilter)
    {
      this.sibtest = paramNodeFilter;
    }

    public boolean accept(Node paramNode)
    {
      boolean bool;
      int i;
      if (paramNode.getParent() != null)
      {
        NodeList localNodeList = paramNode.getParent().getChildren();
        i = 0;
        if (i < localNodeList.size())
          if ((localNodeList.elementAt(i) == paramNode) && (i > 0))
            bool = this.sibtest.accept(localNodeList.elementAt(i - 1));
      }
      while (true)
      {
        while (true)
        {
          return bool;
          ++i;
        }
        bool = false;
      }
    }
  }

  private static class HasAncestorFilter
  implements NodeFilter
  {
    private NodeFilter atest;

    public HasAncestorFilter(NodeFilter paramNodeFilter)
    {
      this.atest = paramNodeFilter;
    }

    public boolean accept(Node paramNode)
    {
      do
      {
        if (paramNode == null)
          break label28;
        paramNode = paramNode.getParent();
      }
      while (!(this.atest.accept(paramNode)));
      int i = 1;
      while (true)
      {
        return i;
        label28: i = 0;
      }
    }
  }
}